package client;

import java.io.IOException;

import shared.CollectionData;
import shared.TransactionData;

/**
 * class CCCS acts as controller and handles menus and sub menus
 * 
 * @author Rasmus Stjerne, Joachim Lindholm, Daniel Gramstrup, Pamela Pedersen
 */
public class CCCS {
	private boolean userAuthenticated; // whether user is authenticated
	private String currentAccountName; // current user's accountname
	private String currentName; // current user's name
	private Display display; // CCCS's display
	private Input input; // CCCS's input
	private Connection connection; // CCCS's connection to the server
	private CollectionData cd;
	private boolean connected = false;
	private String serverIP;

	// constants corresponding to main menu options
	private static final int BALANCE = 1;
	private static final int SPEND = 2;
	private static final int INSERT = 3;
	private static final int TRANSFER = 4;
	private static final int EXIT = 5;

	// constants corresponding to submenu transfer options
	private static final int PROCESS_TRANSACTIONS = 1;
	private static final int SEND_TRANSACTION = 2;
	private static final int BACK_TRANSACTION = 3;

	/**
	 * no-argument CCCS constructor initializes instance variables
	 */
	public CCCS(String ip) {
		userAuthenticated = false; // user is not authenticated to start
		currentAccountName = ""; // no current accountname to start
		currentName = ""; // no current name to start
		display = new Display(); // create display
		input = new Input(); // create input
		connection = new Connection(); // create connection to server
	}

	/**
	 * start CCCS and run the system
	 */
	public void run() {
		display.displayMessageLine("Welcome to the CCCS! \n");
		// connect to the server
		while (!connected) {
			testConnectionToServer();
			// welcome and authenticate user; perform transactions
			while (connected) {
				connection.connect(serverIP);
				// loop while user is not yet authenticated
				while (!userAuthenticated) {
					display.displayMessageLine("\nPlease login!");
					authenticateUser(); // authenticate user
				}
				performTransactions(); // user is now authenticated
				userAuthenticated = false; // reset before next CCCS session
				currentAccountName = ""; // reset before next CCCS session
				display.displayMessageLine("\nThank you! Goodbye!");
			}
		}
	}

	/**
	 * attempts to connect to a server with Ip from user
	 */
	private void testConnectionToServer() {
		try {
			serverIP = input.getServerIp();
			connection.testConnection(serverIP);
			connected = true;
		} catch (IOException ce) {
			System.err
					.println("\n !!! Cannot reach host - please check ip address");
		}
	}

	/**
	 * attempts to authenticate user against database
	 */
	private void authenticateUser() {
		display.displayMessage("\nPlease enter your cbs account name: ");
		String inputName = input.getString();
		display.displayMessage("\nEnter your Password: ");
		String inputPassword = input.getString();
		// authenticates user on the server
		userAuthenticated = connection.login(inputName, inputPassword);
		// check whether authentication succeeded
		if (userAuthenticated) {
			currentAccountName = inputName; // save user's account name
			// gets the name from the accountName
			currentName = connection.getName(currentAccountName);
			display.displayMessageLine("\nWelcome " + currentName);
			display.displayMessageLine("CC KM Factor = "
					+ connection.getCCKMFactor()); // gets the CCKMFACTOR from
													// server
		} else
			display.displayMessageLine("Incorrect info or you are already logged in.");
	}

	/**
	 * display the main menu and perform transactions
	 */
	private void performTransactions() {
		Transaction currentTransaction = null;
		boolean userExited = false;
		while (!userExited) {
			// show main menu and get user selection
			int mainMenuSelection = displayMainMenu();
			switch (mainMenuSelection) {
			// user chose to perform one of three transaction types
			case BALANCE:
			case SPEND:
			case INSERT:
				// initialize as new object of chosen type
				currentTransaction = createTransaction(mainMenuSelection);
				currentTransaction.execute(); // execute transaction
				break;
			case TRANSFER: // user chose to perform a transfer to another user
				displayTransferMenu();
				break;
			case EXIT: // logs the user out and closes the connection
				display.displayMessageLine("\nExiting the system...");
				connection.logout();
				userExited = true;
				break;
			default:
				display.displayMessageLine("\nYou did not enter a valid selection. Try again.");
				break;
			}
		}
	}

	/**
	 * display the main menu and return an input selection
	 * 
	 * @return integer with menu selection
	 */
	private int displayMainMenu() {
		display.displayMessageLine("\nMain menu:");
		display.displayMessageLine(BALANCE + " - View my balance");
		display.displayMessageLine(SPEND + " - Spend CC");
		display.displayMessageLine(INSERT + " - Insert CC");
		display.displayMessageLine(TRANSFER + " - Transfer "
				+ displayNewTransaction());
		display.displayMessageLine(EXIT + " - Exit\n");
		display.displayMessage("Enter a choice: ");
		return input.getInt(BALANCE, EXIT);
	}

	/**
	 * displays an additional message in the main menu if the user has new
	 * transactions
	 * 
	 * @return message to main menu
	 */
	private String displayNewTransaction() {
		cd = connection.getPendingTransactions();
		if (cd.pendingTransactions.size() == 1) {
			return "(" + cd.pendingTransactions.size() + " new transaction)";
		} else if (cd.pendingTransactions.size() > 1) {
			return "(" + cd.pendingTransactions.size() + " new transactions)";
		}
		return "";
	}

	/**
	 * display the sub menu to execute transfers between users
	 */
	private void displayTransferMenu() {
		display.displayMessageLine("\nTransfer menu:");
		display.displayMessageLine(PROCESS_TRANSACTIONS
				+ " - Process transaction (" + cd.pendingTransactions.size()
				+ ")");
		display.displayMessageLine(SEND_TRANSACTION + " - Send new transaction");
		display.displayMessageLine("3 - Back to main menu");
		display.displayMessage("Enter a choice: ");
		int choice = input.getInt(PROCESS_TRANSACTIONS, 3); // return user's
															// selection
		switch (choice) {
		case PROCESS_TRANSACTIONS:
			while (choice != 3) {
				// gets the pending transactions from the server
				cd = connection.getPendingTransactions();
				if (cd.pendingTransactions.size() > 0) {
					display.displayMessageLine("Transaction list");
					display.displayTransactions(cd);
					display.displayMessage("\nSelect transaction to process: ");
					// gets the users choice
					int transactionChoice = input.getInt(1,
							cd.pendingTransactions.size()) - 1;
					TransactionData td = cd.pendingTransactions
							.get(transactionChoice);
					// displays process menu
					display.displayMessageLine("\nProcess menu:");
					display.displayMessageLine("1 - Accept");
					display.displayMessageLine("2 - Decline");
					display.displayMessageLine("3 - Back to overview");
					display.displayMessage("Choose accept/decline: ");
					choice = input.getInt(1, 3);
					if (choice == 1) {
						// accepts the transaction
						connection.acceptTransaction(transactionChoice);
						display.displayMessageLine("\nTransaction accepted from "
								+ td.name + ".");
					} else if (choice == 2) {
						// declines the transaction
						connection.declineTransaction(transactionChoice);
						display.displayMessageLine("\nTransaction declined from "
								+ td.name);
					}
				} else {
					display.displayMessageLine("\nThere are 0 transactions to process.");
					break;
				}
			}
			break;
		case SEND_TRANSACTION:
			display.displayMessage("\nPlease enter the recipients CBS ID (ex. jodo12ab): ");
			// gets the recipient cbs id from the user. Is this wrong, the user
			// will be prompted to re-enter a cbs ID
			String recipient = input.getRecipient(currentAccountName,
					connection);
			display.displayMessage("\nEnter amount to transfer: ");
			// gets the amount from the user
			double amount = input.getDouble(1, connection.transactionLimit());
			// sends the transaction
			connection.sendTransaction(recipient, amount);
			display.displayMessageLine("\n" + amount
					+ " has been transfered to "
					+ connection.getName(recipient)
					+ " and is now awaiting confirmation.");
			break;
		case BACK_TRANSACTION:
			break;
		default:
			display.displayMessageLine("Incorrect input.");
			break;
		}
	}

	/**
	 * Returns an object of specified Transaction subclass
	 * 
	 * @param type
	 *            determines what kind of transaction the user chose. 1. = View
	 *            Balance 2. = Spend CC 3. = Insert CC
	 * @return Object of the specified transaction
	 */
	private Transaction createTransaction(int type) {
		Transaction temp = null;
		switch (type) {
		case BALANCE: // create new Balance transaction
			temp = new Balance(display, connection);
			break;
		case SPEND: // create new Spend transaction
			temp = new Spend(display, connection, input);
			break;
		case INSERT: // create new Insert transaction
			temp = new Insert(display, connection, input);
			break;
		}
		return temp;
	}
}